﻿using System;
using System.Linq;
using DotNetNeural.Data.Learning;
using DotNetNeural.Metrics;

namespace DotNetNeural.Preprocessing
{
    public class LipschitzCalculator
    {
        public IMetric Metric { get; private set; }

        public LipschitzCalculator()
            : this(new DefaultMetric())
        {
        }

        public LipschitzCalculator(IMetric metric)
        {
            if (metric == null)
                throw new NullReferenceException("Illegal null-reference metric");

            Metric = metric;
        }

        public float CalculateLipschitzConstant(ITrainingSet trainingSet)
        {
            return CalculateLipschitzConstant(trainingSet, false);
        }

        public float CalculateLipschitzConstant(ITrainingSet trainingSet, bool includeControlItems)
        {
            if (trainingSet == null)
                throw new NullReferenceException("Illegal null-reference training set");

            float minDeltaX = Single.MaxValue;
            float maxDeltaY = Single.MinValue;

            var learningItems = trainingSet.LearningItems.ToList();
            int count = learningItems.Count;

            for (int i = 0; i < count; ++i)
            {
                for (int j = i; j < count; ++j)
                {
                    if (i != j)
                    {
                        float deltaX = 
                            Metric.CalculateDistance(learningItems[i].Input, learningItems[j].Input);

                        float deltaY =
                            Metric.CalculateDistance(learningItems[i].Output, learningItems[j].Output);

                        if (deltaX < minDeltaX)
                            minDeltaX = deltaX;

                        if (deltaY > maxDeltaY)
                            maxDeltaY = deltaY;
                    }
                }
            }


            return maxDeltaY / minDeltaX;
        }
    }
}
